延續 Docker 啟動 process 的主題,因 container 即 process,因此合理的設計方法會是一個 container 只執行一個 process。而且 Dockerfile 也只能設定一個 ENTRYPOINT 和 CMD,實際上也很難跑多個 process。
但如果真的需要一個 container 同時跑多個 process 的話,該怎麼做呢?
開始前,先來定義簡單的目標:
山不轉,路轉。即然 Dockerfile 或 docker run
不行的話,那就先 docker run
再 docker exec
吧!
# 啟動 Apache
docker run --rm -it --name test httpd:2.4-alpine
# 啟動 top
docker exec -it test top
這個方法非常單純易懂,但代價也是龐大的。這個做法的問題點昨天有提到,主要是 docker exec
的 process 在 PID 1 process 結束的時候,沒有辦法正常地收到 SIGTERM
通知。
再來另一個也非常麻煩的問題是,無法使用「一個」 Docker 指令完成啟動兩個 process,即使是 Docker Compose 也一樣。代表這需要靠腳本或其他方法來組合 Docker 指令,這將會讓維運 container 的人吃盡苦頭。
若 docker exec
不適合的話,那接下來也只剩一種方向:在 CMD 或 ENTRYPOINT 執行某個特別的腳本或程式,由它來啟動所有需要的 process。
寫一個 shell script,讓它去啟動每個 process ,然後在結尾跑一個無窮迴圈即可:
#!/bin/sh
# 啟動 Apache
httpd-foreground &
# 啟動 top
top -b
# 無窮迴圈
while [[ true ]]; do
sleep 1
done
這個方法有一樣的問題--process 無法收到 SIGTERM
信號,而且比 docker exec
更加嚴重。docker exec
的方法,至少還會有一個 process 收到信號,而 shell script 方法則是所有 process 都收不到信號。
筆者有用類似的概念實作 Chromium + PHP for Docker:
#!/bin/sh
set -e
# 若有帶參數的話,則 chromedriver 會作為背景執行,然後再直接執行需要的 process。
if [ "$1" != "" ]; then
chromedriver > /var/log/chromedriver.log 2>&1 &
exec $@
fi
chromedriver
Supervisor 是一個能控制多個 process 執行的管理器。以 Supervisor 改寫 Dockerfile 如下:
FROM httpd:2.4-alpine
RUN apk add --no-cache supervisor
COPY ./supervisord.conf /etc/supervisor.d/supervisord.ini
CMD ["supervisord", "-c", "/etc/supervisord.conf"]
這裡看到還有個設定是 supervisord.conf
,內容如下:
[supervisord]
nodaemon=true
[program:apache2]
command=httpd-foreground
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
[program:top]
command=top -b
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
從執行結果的 log 可以看到 Supervisor 是跑在 PID 1,而 Apache 跑在 PID 8、top 跑在 PID 9 ,兩個 process 都有正常啟動。
2020-10-12 15:24:38,955 INFO supervisord started with pid 1
2020-10-12 15:24:39,961 INFO spawned: 'apache2' with pid 8
2020-10-12 15:24:39,965 INFO spawned: 'top' with pid 9
接著故意使用 Ctrl + C 來中止程式,可以發現 Supervisor 有把 SIGTERM 信號傳送給 Apache 與 top,並把中止程式的任務完成:
2020-10-12 15:24:52,055 WARN received SIGINT indicating exit request
2020-10-12 15:24:52,057 INFO waiting for apache2, top to die
2020-10-12 15:24:52,059 INFO stopped: top (terminated by SIGTERM)
2020-10-12 15:24:52,088 INFO stopped: apache2 (exit status 0)
從今天的範例可以發現,想要在同一個 container 跑多個 process 是很困難的。因此,最好的方法就是:
一個 container 只執行一個 process!